
#include "SendPointHandlerBase.h"
#include "CmdHandler3.h"
#include "Registry.h"
#include "ActorTable.h"
#include "CmdManager.h"
#include "SelectHandlerBase.h"
#include "CmdLineCtrl.h"
#include "QtOSGWidget.h"
#include "Scene.h"
#include "SelectHandler3D.h"
#include "Qt3DPicker.h"
#include "OsgUtil.h"
#include "Plane.h"
#include "Scene.h"
#include "ShxText.h"
#include "GLSLPrograms.h"
#include "Arc2D.h"
#include "mainwindow.h"
#include "GetTriangles.h"

CmdHandler3::CmdHandler3()
{
}

CmdHandler3::~CmdHandler3()
{
	auto m = cmdManager();
	if (m)
	{
		auto sph = m->getSendPointHandler();
		if (sph)
			sph->setPointMask(0);
		if (m_clearSelectionOnExit)
		{
			auto selector = qobject_cast<SelectHandlerBase*>(m->getSelectHandler().data());
			if (selector)
				selector->clearSelection();
		}
	}
	auto picker = getPicker();
	if (picker)
		picker->setPickMode(POINT_MODE);

	// 卸载橡皮筋
	Scene* scene = getScene();
	if (scene)
	{
		if (m_rubberLine3D || m_rubberLine)
		{
			auto widget3D = getWidget();
			widget3D->pauseRenderThread();

			auto sceneGroup = scene->getRep3D()->asGroup();
			if (sceneGroup && m_rubberLine3D)
			{
				sceneGroup->removeChild(m_rubberLine3D);
			}

			auto ucs = scene->getCurrentUCS();
			if (ucs && m_rubberLine)
			{
				auto group = ucs->getRep3D()->asGroup();
				if (group)
				{
					// removeChild会自动释放m_rubberLine
					group->removeChild(m_rubberLine);
				}
			}

			widget3D->resumeRenderThread();
		}

		std::vector<std::shared_ptr<UCS>> ucses = scene->getUCS();
		scene->restoreBuild3DXPoints();
		for (const auto& ucs : ucses)
			ucs->restoreBuildXPoints();

	}
}

void CmdHandler3::onInit()
{
	auto scene = getScene();
	scene->pauseBuild3DXPoints();
	std::vector<std::shared_ptr<UCS>> ucses = scene->getUCS();
	for (const auto& ucs : ucses)
		ucs->pauseBuildXPoints();
}

QtOSGWidget * CmdHandler3::getWidget() const
{
	return static_cast<QtOSGWidget*>(CmdHandler::getWidget());
}

Scene* CmdHandler3::getScene() const
{
	auto widget3D = getWidget();
	if (widget3D)
		return static_cast<Scene*>(widget3D->getIRoot());
	return nullptr;
}

Qt3DPicker * CmdHandler3::getPicker() const
{
	auto widget3D = getWidget();
	if (widget3D)
		return widget3D->getPicker();
	return nullptr;
}

void CmdHandler3::showRubberLine(bool val)
{
	if (val)
	{
		if (!m_rubberLine)
		{
			createRubberLine();
		}
	}
	if (m_rubberLine)
	{
		m_rubberLine->setNodeMask(val ? NoPickMask : 0);
	}
}

void CmdHandler3::setRubberLineP1(const osg::Vec3d & pt)
{
	m_Point = pt;

	if (!m_rubberLine)
	{
		createRubberLine();
	}
	updateRubberLine();
}

void CmdHandler3::setRubberLineP2(const osg::Vec3d & pt)
{
	m_Point2 = pt;

	if (!m_rubberLine)
	{
		createRubberLine();
	}
	updateRubberLine();
}

void CmdHandler3::show3DRubberLine(bool val)
{
	if (val)
	{
		if (!m_rubberLine3D)
		{
			create3DRubberLine();
		}
	}
	if (m_rubberLine3D)
	{
		m_rubberLine3D->setNodeMask(val ? NoPickMask : 0);
	}
}

void CmdHandler3::set3DRubberLineP1(const osg::Vec3d & pt)
{
	m_3DPoint = pt;

	if (!m_rubberLine3D)
	{
		create3DRubberLine();
	}
	update3DRubberLine();
}

void CmdHandler3::set3DRubberLineP2(const osg::Vec3d & pt)
{
	m_3DPoint2 = pt;

	if (!m_rubberLine3D)
	{
		create3DRubberLine();
	}
	update3DRubberLine();
}

bool CmdHandler3::getPoint2D(const Plane & plane, const QVariant & data, const QString& rawInput, CommandSource cs, osg::Vec3d& ucspt)
{
	if (data.type() == QVariant::Vector3D)
	{
		auto pt = data.value<QVector3D>();
		if (cs == CS_LBTNDOWN || cs == CS_MOUSEMOVE || cs == CS_LBTNUP)
		{
			osg::Vec3d tmp;
			bool success = getViewPointOnPlane(plane, { pt.x(), pt.y(), pt.z() }, tmp);
			ucspt = tmp;
			if (success)
			{
				ucspt.z() = 0;
				if (cs != CS_MOUSEMOVE)
				{
					remark(QString("Point2D = %1, %2").arg(ucspt.x(), 0, 'f', 4).arg(ucspt.y(), 0, 'f', 4));
					updateLastPoint(plane, ucspt);
				}
				return true;
			}
		}
		else
		{
			auto matrix_1 = osg::Matrixd::inverse(plane.getMatrix());
			ucspt = osg::Vec3d(pt.x(), pt.y(), pt.z()) * matrix_1;
			ucspt.z() = 0;
			remark(QString("Point2D = %1, %2").arg(ucspt.x(), 0, 'f', 4).arg(ucspt.y(), 0, 'f', 4));
			updateLastPoint(plane, ucspt);
			return true;
		}
	}
	else if(data.type() == QVariant::PointF)
	{
		QPointF point;
		QPointF basePoint;
		osg::Vec3d dir = m_Point2 - m_Point;
		if (dir.normalize() == 0)
		{
			// 有些二维编辑命令使用了三维橡皮筋，需要转换到二维平面
			auto matrix_1 = osg::Matrixd::inverse(plane.getMatrix());
			auto tmp = m_3DPoint * matrix_1;
			basePoint = QPointF(tmp.x(), tmp.y());
		}
		else
		{
			basePoint = QPointF(m_Point.x(), m_Point.y());
		}
		StrToPoint(rawInput, basePoint, point);
		//QPointF point = data.toPointF();
		ucspt.set(point.x(), point.y(), 0);
		updateLastPoint(plane, ucspt);
		return true;
	}
	else if(data.type() == QVariant::Double)
	{
		osg::Vec3d lastPoint = m_Point;
		osg::Vec3d dir = m_Point2 - m_Point;
		if (dir.normalize() == 0)
		{
			// 有些二维编辑命令使用了三维橡皮筋，需要转换到二维平面
			dir = m_3DPoint2 - m_3DPoint;
			if (dir.normalize() == 0)
				return false;
			auto matrix_1 = osg::Matrixd::inverse(plane.getMatrix());
			// 方向变换，不是点变换，所以去掉平移分量。注意transform3x3参数的顺序
			dir = osg::Matrixd::transform3x3(dir, matrix_1);
			lastPoint = m_3DPoint * matrix_1;
		}
		double len = data.toDouble();
		ucspt.set(lastPoint.x() + len * dir.x(), lastPoint.y() + len * dir.y(), 0);

		updateLastPoint(plane, ucspt);

		return true;
	}
	return false;
}

osg::Vec4 CmdHandler3::getCurrentColor()
{
	Registry &reg = Registry::instance();
	MainWindow * main = reg.get<MainWindow>(mainWindowID);
	return main->getCurrentColor();
}

void CmdHandler3::createRubberLine()
{
	Scene* scene = getScene();
	if (scene)
	{
		auto widget3D = getWidget();
		widget3D->pauseRenderThread();

		auto ucs = scene->getCurrentUCS();
		if (ucs)
		{
			auto group = ucs->getRep3D()->asGroup();
			if (group)
			{
				auto scene = getScene();
				auto ucs = scene->getCurrentUCS();
				m_rubberLine = new osg::Group();
				m_lines = new osg::Geometry();
				osg::ref_ptr<osg::Program> program = createEdgeProgram();
				auto ss = m_lines->getOrCreateStateSet();
				ss->setAttributeAndModes(program, osg::StateAttribute::ON);

				m_rubberLine->addChild(m_lines);
				m_lenText = new ShxText();
				m_lenText->setCharacterSize(12);
				m_lenText->setCharacterSizeMode(ShxText::SCREEN_COORDS);
				m_lenText->setFontFile("monotxt.shx", "hztxt.shx");
				m_rubberLine->addChild(m_lenText);
				m_angleText = new ShxText();
				m_angleText->setCharacterSize(12);
				m_angleText->setCharacterSizeMode(ShxText::SCREEN_COORDS);
				m_angleText->setFontFile("monotxt.shx", "hztxt.shx");
				m_rubberLine->addChild(m_angleText);
				m_rubberLine->setNodeMask(0);
				group->addChild(m_rubberLine);
			}
		}

		widget3D->resumeRenderThread();
	}
}

void CmdHandler3::create3DRubberLine()
{
	if (m_rubberLine3D)
		return;

	Scene* scene = getScene();
	if (scene)
	{
		auto widget3D = getWidget();
		widget3D->pauseRenderThread();

		auto sceneGroup = scene->getRep3D()->asGroup();
		if (sceneGroup)
		{
			m_rubberLine3D = new osg::Group();
			m_line3D = new osg::Geometry();
			osg::ref_ptr<osg::Program> program = createEdgeProgram();
			auto ss = m_line3D->getOrCreateStateSet();
			ss->setAttributeAndModes(program, osg::StateAttribute::ON);

			m_rubberLine3D->addChild(m_line3D);
			m_rubberLine3D->setNodeMask(0);
			sceneGroup->addChild(m_rubberLine3D);
		}

		widget3D->resumeRenderThread();
	}
}

void CmdHandler3::updateRubberLine()
{
	auto widget3D = getWidget();
	widget3D->pauseRenderThread();
	// TODO
	const double dx = (m_Point2.x() - m_Point.x());
	const double dy = (m_Point2.y() - m_Point.y());
	const double len = sqrt(dx * dx + dy * dy);
	//得到水平方向的点
	const double x = m_Point.x() + len;
	const double y = m_Point.y();
	osg::Vec3d horizontalPt;
	horizontalPt.set(x, y, 0);
	//设置长度文字
	const double lenX = (m_Point2.x() + m_Point.x()) / 2;
	const double lenY = (m_Point2.y() + m_Point.y()) / 2;
	osg::Vec3d lenTextPt;
	lenTextPt.set(lenX, lenY, 0);
	m_lenText->setText(std::_Floating_to_wstring(L"%.3f", len));
	m_lenText->setPosition(lenTextPt);
	//设置角度文字
    double angle = atan2(m_Point2.y() - m_Point.y(), m_Point2.x() - m_Point.x());
	const int degree = angle / M_PI * 180;
	m_angleText->setText(std::to_wstring(degree)+ std::wstring(L"°"));
	const double angleX = m_Point.x() + len * cos(angle / 2);
	const double angleY = m_Point.y() + len * sin(angle / 2);
	osg::Vec3d angleTextPt;
	angleTextPt.set(angleX, angleY, 0);
	m_angleText->setPosition(angleTextPt);
	m_angleText->setAlignment(ShxText::CENTER_CENTER);

	auto va = new osg::Vec3Array();
	va->push_back(m_Point);
	if (angle < 0.0)
	{
		va->push_back(horizontalPt);
		va->push_back(m_Point);
		va->push_back(m_Point2);
		//得到圆弧上的点
		auto points = getArcPoints(angle, 0, len, m_Point, osg::Vec3d(0, 0, 1), 3);
		appendPoints(va, points);
	}
	else
	{
		va->push_back(m_Point2);
		va->push_back(m_Point);
		va->push_back(horizontalPt);
		//得到圆弧上的点
		auto points = getArcPoints(0, angle, len, m_Point, osg::Vec3d(0, 0, 1), 3);
		appendPoints(va, points);
	}
	m_lines->setVertexArray(va);

	const osg::Vec4f& color = { 0,0,0,1 };
	osg::ref_ptr<osg::Vec4Array> ca = new osg::Vec4Array();
	ca->push_back(color);
	m_lines->setColorArray(ca.get(), osg::Array::BIND_OVERALL);

	m_lines->getPrimitiveSetList().clear();
	m_lines->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINE_STRIP, 0, va->size()));
	m_lines->dirtyGLObjects();

	widget3D->resumeRenderThread();
}

void CmdHandler3::update3DRubberLine()
{
	auto widget3D = getWidget();
	widget3D->pauseRenderThread();

	auto va = new osg::Vec3Array();
	va->push_back(m_3DPoint);
	va->push_back(m_3DPoint2);

	m_line3D->setVertexArray(va);

	const osg::Vec4f& color = { 0,0,0,1 };
	osg::ref_ptr<osg::Vec4Array> ca = new osg::Vec4Array();
	ca->push_back(color);
	m_line3D->setColorArray(ca.get(), osg::Array::BIND_OVERALL);

	m_line3D->getPrimitiveSetList().clear();
	m_line3D->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINE_STRIP, 0, va->size()));
	m_line3D->dirtyGLObjects();

	widget3D->resumeRenderThread();
}

// 设置SendPointHandler的上一点，保证正交捕捉的正确性
void CmdHandler3::updateLastPoint(const Plane & plane, const osg::Vec3d & ucspt)
{
	auto sph = getSendPointHandler();
	QVector3D lastPos;
	auto zDir = plane.zAxis();
	if (zDir == osg::Vec3d(0, 0, 1))
	{
		lastPos = QVector3D(ucspt.x(), ucspt.y(), 0);
	}
	else if (zDir == osg::Vec3d(1, 0, 0))
	{
		lastPos = QVector3D(0, ucspt.x(), ucspt.y());
	}
	else if (zDir == osg::Vec3d(0, -1, 0))
	{
		lastPos = QVector3D(ucspt.x(), 0, ucspt.y());
	}
	sph->setLastPoint(lastPos);
	if (isRubberLine2DVisible())
	{
		setRubberLineP1(ucspt);
	}
	if (isRubberLine3DVisible())
	{
		set3DRubberLineP1(osg::Vec3d(lastPos.x(), lastPos.y(), lastPos.z()));
	}
}

bool CmdHandler3::isRubberLine2DVisible() const
{
	return m_rubberLine && m_rubberLine->getNodeMask() != 0;
}

bool CmdHandler3::isRubberLine3DVisible() const
{
	return m_rubberLine3D && m_rubberLine3D->getNodeMask() != 0;
}

bool CmdHandler3::getNearFarPoint(const osg::Vec3d & worldPt, osg::Vec3d & nearPt, osg::Vec3d & farPt)
{
	auto widget3D = getWidget();
	osg::Camera* camera = widget3D->getCamera();
	if (!camera)
		return false;
	osg::Matrix VPW, invVPW;
	bool success = getCameraViewProjectionWindowMatrix(camera, VPW, invVPW);
	if (!success)
		return false;
	osg::Vec3d dcsPt = worldPt * VPW;
	getFarNearPoint(dcsPt.x(), dcsPt.y(), invVPW, nearPt, farPt);
	return true;
}

bool CmdHandler3::getViewPointOnPlane(const Plane& plane, const osg::Vec3d& worldPt, osg::Vec3d& planePt)
{
	osg::Vec3d nearPt, farPt;
	osg::Vec3d resWorldPt;
	bool success = getNearFarPoint(worldPt, nearPt, farPt);
	success = success && xPlaneLine(plane.origin(), plane.zAxis(), nearPt, farPt, resWorldPt);
	if (!success)
		return false;
	auto m = plane.getMatrix();
	m = osg::Matrixd::inverse(m);
	planePt = resWorldPt * m;
	planePt.z() = 0;
	return true;
}

bool CmdHandler3::getViewPointOnPlane(const Plane& plane, const osg::Vec2d& dcsPt, const osg::Matrixd& invVPW, osg::Vec3d& planePt)
{
	osg::Vec3d nearPt, farPt;
	osg::Vec3d resWorldPt;
	getFarNearPoint(dcsPt.x(), dcsPt.y(), invVPW, nearPt, farPt);
	bool success = xPlaneLine(plane.origin(), plane.zAxis(), nearPt, farPt, resWorldPt);
	if (!success)
		return false;
	auto m = plane.getMatrix();
	m = osg::Matrixd::inverse(m);
	planePt = resWorldPt * m;
	planePt.z() = 0;
	return true;
}

void getProjPointOnPlane(const Plane& plane, const osg::Vec3d& worldPt, osg::Vec3d& planePt)
{
	auto v = worldPt - plane.origin();
	auto v2Len = v * plane.zAxis();
	auto v2 = plane.zAxis() * v2Len;
	auto v1 = v - v2;
	planePt = plane.origin() + v1;
}
